#ifndef ES_SERIALWIFI_H_
#define ES_SERIALWIFI_H_
//wtf
#define CON_WIFI "AT+CWJAP=\"dd-wrt\",\"oploplopoploplop\"\r\n"
#define CON_TCP "AT+CIPSTART=\"TCP\",\"192.168.1.238\",8080\r\n"

#define AT_BUFFER_SIZE 	100

#define min(a,b) a<b ? a : b

#include <stdio.h>
#include "es_blockDelay.h"

#include "es_usart.h"
#include "stm_std_lib.h"

/*
说明： 
	已知bug: 
		1.数据长度超过 AT_BUFFER_SIZE 会导致stm死机
		2.在某些情况下esp8266会重启，（可能是串口连接不牢（浮空）导致的，这个有待确认）
			如果重启，本程序不会重连，如果以后发现这个问题影响使用，可以再做处理
	使用方法：
		1.修改 CON_WIFI、CON_TCP这两个连接字符串
		2.先Init(void pack(u8* buff, u8 size), CUsart* usart )
		3.然后Connect();
		4.然后WriteCommand(u8* data, u8 len)发送数据
		5.收到数据会回传到main的void wifiDataReady(u8* buff, u8 len)
		 
*/

inline void strcpy(char* str1, char* str2, u8 len)
{
	for(int i=0;i<len;i++)
	{
		str1[i] = str2[i];
	}
}

inline int strlen(char* data)
{
	for(int i=0;i<AT_BUFFER_SIZE;i++)
	{
		if(data[i]==0) return i;
	}
	return 0;
}
inline bool strncmp(char* a, char* b, u8 len)
{
	for(int i =0;i<len;i++)
	{
		if(a[i]!=b[i])return false;
	}
	return true;
}
inline int power(int a, int x)
{
	int result =1;
	while(x!=0)
	{
		result *= a;
		x--;
	}
	return result;
}
inline int strtoint(char* str, int len)
{
	int result =0;
	for(int i=0;i<len;i++)
	{
		result += (str[i]-'0')*power(10,len-i-1);
	}
	return result;
}

void (*DataReady)(u8*, u8);




enum SerialWifiState {initiated=0, ConnectingWifi, 
WIFI, ConnectingTCP, TCP, idle, Rx_len, Rx_text, Tx_cmd, Tx, fail};


class es_SerialWifi
{
private:

	u8* buffer;
	u8* bufferDataPtr;
	u32 counter;
	CUsart* serialport;
	
	u8* writeBuffer;
	u8 writeLen;

	u8 readLen;

//	char OK[] = {'O','K'};
	
	inline void pointerDisplacement(u8* &pointer, int val)
	{
		for(int i = 0;i<val;i++)pointer++;
	}
	//status recorder

	inline void connectingWIFIHandler(u8* string, u8 len)
	{
		if(string[0]=='O' && string[1]=='K')
		{
			state = ConnectingTCP;
			serialport->Write(CON_TCP, sizeof(CON_TCP));
			return;
		}
		//else if(strncmp("FAIL",(char*)string,min(sizeof("FAIL")-1, len)))
		else if(string[0]=='F' &&string[1]=='A' &&string[2]=='I' &&string[3]=='L')
		{
			Connect();
		}
	}
	inline void connectingTCPHandler(u8* string, u8 len)
	{
		if(string[0]=='O' && string[1]=='K')
		{
			state = idle;
			return;
		}
		if(string[0]=='C' && string[1]=='L' && string[2]=='O'&&string[3]=='S' && string[4]=='E' && string[5]=='D')
		{
			Connect();
		}
	}
	inline void idleHandler(u8* string, u8 len)
	{


	}
	inline void tx_cmdHandler(u8* string, u8 len)
	{
		//tx data after found '>'
		state = idle;
		serialport->Write(writeBuffer,writeLen);
	}

public:
		u8 state;
CUsart* debugger;
	inline void Init(void pack(u8* buff, u8 size), CUsart* usart )
	{
		counter =0;
		
		DataReady = pack;
		
		state = initiated;
		//init. buffer
		buffer = new u8[AT_BUFFER_SIZE];
		writeBuffer = new u8[AT_BUFFER_SIZE];
		serialport = usart;
		writeLen = 0;
		readLen=0;
	}
	inline void Init(void pack(u8* buff, u8 size), CUsart* usart, CUsart*  usart2)
	{
		counter =0;
		
		DataReady = pack;
		
		state = initiated;
		//init. buffer
		buffer = new u8[AT_BUFFER_SIZE];
		writeBuffer = new u8[AT_BUFFER_SIZE];
		serialport = usart;
		writeLen = 0;
		debugger = usart2;
		readLen=0;
	}
	//connect to both WIFI and TCP
  inline void Connect()
	{
		serialport->Write(CON_WIFI, sizeof(CON_WIFI));
		state = ConnectingWifi;
	}
	inline void NewByte(u8 val)
	{
		AppendBuffer(val);
	}
	//handle incoming commands
	inline void CommandHandler(u8* string, u8 len)
	{
		switch(state)
		{
			case initiated: 
				break;
			case ConnectingWifi: 
				connectingWIFIHandler(string, len);
				break; 
			case ConnectingTCP: 
				connectingTCPHandler(string, len);
				break; 
			case Tx_cmd: 
				tx_cmdHandler(string, len);
				break; 
			case fail:
				break;
		}

	}
	
		//add new data to the end of buffer, and if found a complete command, call the handler
	inline void AppendBuffer(u8 val)
	{
		buffer[counter%AT_BUFFER_SIZE] = val;
		
		//todo 
		//debugger->Write(val);
		if(state==Tx_cmd && buffer[counter%AT_BUFFER_SIZE-1] == 0x3e && buffer[counter%AT_BUFFER_SIZE]==0x20)
		{
		//	delay_ms(20);
			//debugger->Write(0xfc);
			tx_cmdHandler(&val, 0);
			counter = 0;
			for(int i=0;i<AT_BUFFER_SIZE;i++) buffer[i]=0;
			return;
		}
	
		if(state == Rx_len && buffer[counter]==':')
		{
			//buffer[counter] = 0;
			
			state = Rx_text;
			readLen = strtoint((char*)buffer, counter);
			counter=0;
			

			return;
		}
		if(state == Rx_text && counter>=readLen-1)
		{
			state = idle;
			DataReady(buffer, readLen);
			counter =0;
			
			
			return;
		}
		if(	state == idle && counter>3	&&
			buffer[counter-4]=='+' &&
			buffer[counter-3]=='I' &&
			buffer[counter-2]=='P' &&
			buffer[counter-1]=='D' &&
			buffer[counter]==',')
		{
			state= Rx_len;
			counter =0;
			return;
		}

		
		//match the pattern .*\r\n
		if(state!=idle && state!=Rx_len && state!=Rx_text && buffer[counter%AT_BUFFER_SIZE]=='\n' && buffer[counter%AT_BUFFER_SIZE-1]=='\r' )
		{
				//debugger->Write(buffer, counter-1);
			
				CommandHandler(buffer, counter-1);

				counter = 0;
				for(int i=0;i<AT_BUFFER_SIZE;i++) buffer[i]=0;
				return;
		}
		
		counter++;
		if(counter>=AT_BUFFER_SIZE) counter=0;
	}
	//write AT command
	inline void WriteCommand(u8* data, u8 len)
	{
		if(state==idle)
		{
			state = Tx_cmd;
			char cmdSend[50]="";	
			char cmdFormat[]="AT+CIPSEND=%d\r\n";
			sprintf(cmdSend,cmdFormat,len);
		
			serialport->Write(cmdSend, strlen(cmdSend));
			//delay_ms(20);
			
			
			//serialport->Write(data, len);
			strcpy((char*)writeBuffer, (char*)data, len);
			writeLen = len;
		}
	}

};

#endif
